/*
 * nume: message.h
 * autor: Dan
 * - descriere: clasa mesaj, face legatura intre modulele care
 * indexeaza si prelucreaza datele si modulele de retea.
 * - clasele mesaj se pun in interiorul 'namespace messages'.
 * - un mesaj are forma:
 *      length quint16 lungimea mesajului
 *      hash quint16 error detection code
 *      type quint8 tipul mesajului
 *      campuri ? campurile specfice fiecarui mesaj
 * - mesajele sunt transmise peste tcp/upd care asigura corectitudinea
 * mesajului (deci nu avem nevoie de de crc)
 * - pentru a construi un mesaj gol utilizam clasa Message
 *   ex:
 *   Message* mesaj = new Message(messages::ACK_LOGIN);
 * - pentru definirea unui nou tip de mesaj
 * trebuie sa avem o functie statica numita instance() care creaza
 * un nou obiect de tipul respectiv. functia nu ia nici un parametru si
 * intoarece un Message*. clasa va fi inregistrata utilizand
 * REGISTER_MESSAGE_TYPE
 *
 * tabela de modificarei
 *   13 iun 2007 - ~D - prima versiune
 *   21 iun 2007 - ~D - implementat mesajul login
 *   25 iun 2007 - Alexandru - redesigned
 */

#ifndef MESSAGE_H__
#define MESSAGE_H__

#include "../base/client-id.h"
#include <QtCore>

class QByteArray;
class FingerPrint;

class Message {
public:
    // functia este chemata la inceputul programului
    // cu identificatorul clientului
    static void set_default_source(ClientId source) {
        default_source_ = source;
    }

    // construieste un mesaj gol
    Message(int type) :
        type_(type), source_(default_source_) {
    }

    virtual ~Message() {
    }

    // intoarce tipul mesajul
    int type() const {
        return type_;
    }

    ClientId source() const {
        return source_;
    }

    // parseaza intrarea pentru un mesaj de tipul instantei.
    // daca mesajul nu este valid, atunci programul este inchis
    // cu un mesaj de eroare corespunzator.
    void parse_array(const QByteArray* array);

    // construieste un vector de bytes din mesaj
    // apelatul devine stapan pe rezultat si este obligat
    // sa dealoce memoria
    const QByteArray* build_array() const;

protected:
    virtual void parse_array(const QByteArray* array, int* position);
    virtual void build_array(QByteArray* array) const;

private:
    static ClientId default_source_;

    quint16 length_;       // lungimea mesajului
    quint8 type_;
    ClientId source_;      // identificatorul celui care trimite mesajul
};

class MessageRegister {
public:
    typedef Message* (*InstanceFunction)();

    // intoarece o instanta a unui mesaj de tipul type
    // daca type nu este cunoscut intoarce chiar un Message
    static Message* instantiate(int type);

    MessageRegister(int type, InstanceFunction function);
private:
};

// inregistreaza un nou tip de mesaje prin asocierea 'type - class_name'
// exemplu REGISTER_MESSAGE_TYPE(S_QUERY, SQuery::instance);
// acest define trebuie sa apara in surse (.cc)
#define REGISTER_MESSAGE_TYPE(type, class_name) \
    static Message* instance_##type() { \
        LOG(INFO) << "Instance of type = " << #type << "(" << int(type) \
                  << "), class = " << #class_name; \
        return new class_name; \
    } \
    static MessageRegister register_##type(type, instance_##type);

namespace messages {

class ByteArrayAppender {
public:
    explicit ByteArrayAppender(QByteArray* array) :
        array_(array) {
    }

    void append(quint8 value);
    void append(quint16 value);
    void append(quint32 value);
    void append(quint64 value);
    void append(const FingerPrint& value);
    void append(const QByteArray& value);

private:
    QByteArray* array_;
};

class ByteArrayParser {
public:
    explicit ByteArrayParser(const QByteArray* array,
                             int* position) :
        array_(array), position_(position) {
    }

    void parse(quint8* value);
    void parse(quint16* value);
    void parse(quint32* value);
    void parse(quint64* value);
    void parse(FingerPrint* value);
    void parse(QByteArray* value);

private:
    const QByteArray* array_;
    int* position_;
};

Message* bytearray_to_message(QByteArray* array);

}

/* Scheleton

// HELLO
REGISTER_MESSAGE_TYPE(HELLO, Hello);

Hello::Hello() : Message(HELLO) {
}

Hello::Hello(...) :
    Message(HELLO), ...) {
}

void Hello::build_array(QByteArray* array) const {
    Message::build_array(array);

    ByteArrayAppender a(array);
}

void Hello::parse_array(const QByteArray* array, int* position) {
    Message::parse_array(array, position);

    ByteArrayParser p(array, position);
}

*/

#endif
